local EffectLogic = _G.is_world or require("scene.EffectLogic")
local ExcelMap = require("excel.Map")
local map_mgr = _G.map_mgr
local MapEx = require("core.MapEx")
local Msg = require("core.Msg")
local Obj = _G.is_world or require("core.Obj")
local ExcelNpc = _G.is_world or require("excel.Npc").npc

MAP_TYPE_NORMAL         = 0
MAP_TYPE_MAINCITY       = 1
MAP_TYPE_WORLDBOSS      = 2
MAP_TYPE_WILDBOSS       = 3
MAP_TYPE_COPY           = 4



local SceneProto = require("scene.Proto")

map_id_to_scene_id = {}	-- mapid->{起始sceneid 终点sceneid}
scene_id_to_map_id = {}	-- sceneid->mapid
list_gc_add_obj = list_gc_add_obj or {}	--mapid->{场景内所有npc的GC_ADD_NPC包}
list_monster = {}
local bin_map = {}

HOME_MAP_ID1 = 2002			-- 本服主城ID

local logic_id = _G.logic_id

local function file2str(filename)
    local f = io.open(filename, "rb")
    if not f then
        assert(nil, filename)
    end
    local str = f:read("*a")
    assert(io.close(f))
    return str
end

local function get_jump_point_name(map_id, x, y)
    for _, v in pairs(ExcelMap[map_id]) do
        if v.action_cmd1 == "jump" then
            local rects = getRects(map_id, v.action_arg1[1])
            if not rects then
                assert(nil, map_id)
            end
            for i = 1, #rects do
                local rect = rects[i]
                if rect[1] < x and x < rect[3] and rect[2] < y and y < rect[4] then
                    return "往 " .. ExcelMap.scene[v.action_arg1[2]].name	
                end
            end
        end
    end
    return ""
end
local function isIn(sx, sy, xlist, ylist)
    local t
    for i = 1, #xlist do
        local j = i - 1
        if j < 1 then
            j = #xlist
        end
        local tt = (xlist[i] - sx) * (ylist[j] - sy) - (xlist[j] - sx) * (ylist[i] - sy)
        if t then
            if tt < 0 and 0 < t or 0 < tt and t < 0 then
                return
            end
        else
            t = tt
        end
    end
    return true
end
local function getSj(dotx, doty)
    local tx, ty = {}, {}
    while true do
        if #dotx < 3 then
            return tx, ty
        end
        for i = 1, #dotx do
            local a = i - 1
            local b = i + 1
            if a < 1 then
                a = #dotx
            end
            if #dotx < b then
                b = 1
            end
            local t = (dotx[b] - dotx[a]) * (doty[i] - doty[a]) - (doty[b] - doty[a]) * (dotx[i] - dotx[a])
            local fail
            for j = 1, #dotx do
                if j ~= i and j ~= a and j ~= b then
                    local tt = (dotx[b] - dotx[a]) * (doty[j] - doty[a]) - (doty[b] - doty[a]) * (dotx[j] - dotx[a])
                    if t < 0 and tt < 0 or 0 < t and 0 < tt then
                        fail = true
                        break
                    end
                end
            end
            if not fail then
                tx[#tx + 1] = dotx[i]
                ty[#ty + 1] = doty[i]
                tx[#tx + 1] = dotx[a]
                ty[#ty + 1] = doty[a]
                tx[#tx + 1] = dotx[b]
                ty[#ty + 1] = doty[b]
                local k = 0
                for j = 1, #dotx do
                    if j ~= i then
                        k = k + 1
                        dotx[k] = dotx[j]
                        doty[k] = doty[j]
                    end
                end
                dotx[#dotx] = nil
                doty[#doty] = nil
                break
            end
        end
    end
end
local step = 0
local step2 = 0
local homex, homey, homex2, homey2 = {}, {}, {}, {}
function getHomePoint(mapId)
    if mapId == HOME_MAP_ID2 then
        step2 = step2 + 1
        if #homex2 < step2 then
            step2 = 1
        end
        return homex2[step2], homey2[step2]
    end
    step = step + 1
    if #homex < step then
        step = 1
    end
    return homex[step], homey[step]
end

function getHomeMapID()
    return HOME_MAP_ID1 -- 这里专门写个接口是为了以后跨服回城mapid不一样做准备
end

local function getHomeXy(homex, homey, mapId, dotx, doty)
    local minx = 0x7fffffff
    local maxx = 0
    local miny = minx
    local maxy = maxx
    local str = bin_map[mapId]
    local w = math.ceil(MapEx[mapId].rect[-1][1][3] / 25)
    for i = 1, #dotx do
        if dotx[i] < minx then
            minx = dotx[i]
        end
        if doty[i] < miny then
            miny = doty[i]
        end
        if maxx < dotx[i] then
            maxx = dotx[i]
        end
        if maxy < doty[i] then
            maxy = doty[i]
        end
    end
    local dotx, doty = getSj(dotx, doty)
    local xlist, ylist = {}, {}
    for x = minx, maxx, 50 do
        for y = miny, maxy, 50 do
            if string.byte(str, math.floor(y / 25) * w + math.floor(x / 25) + 1) % 2 == 1 then
                for i = 1, #dotx, 3 do
                    xlist[1] = dotx[i]
                    xlist[2] = dotx[i + 1]
                    xlist[3] = dotx[i + 2]
                    ylist[1] = doty[i]
                    ylist[2] = doty[i + 1]
                    ylist[3] = doty[i + 2]
                    if isIn(x, y, xlist, ylist) then
                        homex[#homex + 1] = x
                        homey[#homey + 1] = y
                        break
                    end
                end
            end
        end
    end
    local len = #homex
    for i = 1, len do
        local j = math.random(1, len)
        homex[i], homex[j] = homex[j], homex[i]
        homey[i], homey[j] = homey[j], homey[i]
    end
end
local function init()
    local next_pos = 1
    for k, v in pairs(ExcelMap.scene) do	  
        map_id_to_scene_id[k] = {next_pos, next_pos + v.scene_count - 1}
        for i = next_pos, next_pos + v.scene_count - 1 do
            scene_id_to_map_id[i] = k
        end
        next_pos = next_pos + v.scene_count
        local cf = MapEx[k]
        local str = file2str("map/" .. k .. ".map")
        local w = math.ceil(cf.rect[-1][1][3]/25)
        local h = math.ceil(cf.rect[-1][1][4]/25)
        bin_map[k] = string.sub(str, 9, 8 + w * h)
        if v.logic_id == logic_id then
            v.logic_id  = v.logic_id or 2
            map_mgr.load(k, w, h, bin_map[k])
            if Msg then
                if MapEx[k].monster then
                    for group, vv in pairs(MapEx[k].monster) do
                        for _, vvv in ipairs(vv) do
                            local id,x,y = vvv[1],vvv[2],vvv[3]
                            list_monster[k] = list_monster[k] or {}
                            list_monster[k][group] = list_monster[k][group] or {}
                            list_monster[k][group][x] = list_monster[k][group][x] or {}
                            if list_monster[k][group][x][y] then
                                assert()
                            end
                            list_monster[k][group][x][y] = id
                        end
                    end
                end
                if not list_gc_add_obj[k] then

                    list_gc_add_obj[k] = {[1] = Msg.proto_id.GC_LIST_DISPLAY_N_NPC, display = {[0] = 0}, npc = {[0] = 0}}
                    if MapEx[k].npc and MapEx[k].npc[0] then
                        for _, vv in ipairs(MapEx[k].npc[0]) do
                            local id, x, y = vv[1], vv[2], vv[3]
                            list_gc_add_obj[k].npc[0] = list_gc_add_obj[k].npc[0] + 1
                            if ExcelNpc and not ExcelNpc[id] then
                                assert(nil, k .. " " .. id)
                            end
                            if Obj.q_pop then
                                list_gc_add_obj[k].npc[list_gc_add_obj[k].npc[0] ] = {obj_id = Obj.q_pop(), head_title = ExcelNpc[id].head_titleicon_id, name = ExcelNpc[id].name, npc_id = id, body_id = ExcelNpc[id].body_id, body_scale = ExcelNpc[id].body_scale, horse_id = ExcelNpc[id].horse, head_id = ExcelNpc[id].head_id, body_height = ExcelNpc[id].body_height,way = ExcelNpc[id].way, x = x, y = y, special_action = ExcelNpc[id].special_action, bindTask1 = ExcelNpc[id].bindTask1, bindTask2 = ExcelNpc[id].bindTask2}
                            end
                            for _, v in ipairs(SceneProto.GC_ADD_NPC) do
                                -- 检查GC_ADD_NPC协议的字段是否和缓存table里面的匹配
                                if not list_gc_add_obj[k].npc[list_gc_add_obj[k].npc[0] ][v[1] ] then
                                    print(v[1], "not exist")
                                end
                            end
                        end
                    end

                    if MapEx[k].display and MapEx[k].display[0] then
                        for _, vv in ipairs(MapEx[k].display[0]) do
                            local id, x, y = vv[1], vv[2], vv[3]
                            list_gc_add_obj[k].display[0] = list_gc_add_obj[k].display[0] + 1
                            if Obj.q_pop then
                                list_gc_add_obj[k].display[list_gc_add_obj[k].display[0] ] = {obj_id = Obj.q_pop(), body_id = id, x = x, y = y, effectConfig = EffectLogic.getConfig(id), name = get_jump_point_name(k, x, y)}
                            end
                            for _, v in ipairs(SceneProto.GC_ADD_DISPLAY) do
                                if not list_gc_add_obj[k].display[list_gc_add_obj[k].display[0] ][v[1] ] then
                                    print(v[1], "not exist")
                                end
                            end
                        end				 
                    end
                end
            end
        end
    end

    getHomeXy(homex, homey, 2002, {4870, 3632, 4831, 6058}, {3517, 2905, 2324, 2993})

    --	getHomeXy(homex, homey, 2002, {950, 3000, 5000, 4550, 3000}, {2000, 1150, 2000, 2500, 2800})
    --	getHomeXy(homex, homey, 9999, {700, 1700, 2500, 1500}, {1300, 600, 1300, 1600})
    --	getHomeXy(homex2, homey2, 1101, {3333, 1614, 3788}, {2888, 2413, 2089})

end
function get_logic_id(mapid)
    return ExcelMap.scene[mapid].logic_id
end

function is_same_logic(mapid)
    return ExcelMap.scene[mapid].logic_id == logic_id
end
local tb={}
function drop_item(scene_id, x, y, cnt, hx, hy)
    local map_id = scene_id_to_map_id[scene_id]
    hx = hx  or x
    hy = hy  or y
    return map_mgr.drop_item(map_id, tb, x, y, cnt, hx, hy) and tb
end

function get_dot(scene_id, x, y)
    local map_id = scene_id_to_map_id[scene_id]
    local str = bin_map[map_id]
    x = math.floor(x / 25)
    y = math.floor(y / 25)
    local w = math.ceil(MapEx[map_id].rect[-1][1][3] / 25)
    return string.byte(str, y * w + x + 1)
end

function isFishArea(scene_id, x, y)
    local map_id = scene_id_to_map_id[scene_id]
    local str = bin_map[map_id]
    x = math.floor(x / 25)
    y = math.floor(y / 25)
    local w = math.ceil(MapEx[map_id].rect[-1][1][3] / 25)
    local n = string.byte(str, y * w + x + 1)
    return n and (n-n%8)/8 % 2 == 0
end

function isLeitaiArea(scene_id, x, y)
    local map_id = scene_id_to_map_id[scene_id]
    local str = bin_map[map_id]
    x = math.floor(x / 25)
    y = math.floor(y / 25)
    local w = math.ceil(MapEx[map_id].rect[-1][1][3] / 25)
    local n = string.byte(str, y * w + x + 1)
    return n and (n-n%16)/16 % 2 == 1
end

function isWalkArea(scene_id, x, y)
    local map_id = scene_id_to_map_id[scene_id]
    local str = bin_map[map_id]
    x = math.floor(x / 25)
    y = math.floor(y / 25)
    local w = math.ceil(MapEx[map_id].rect[-1][1][3] / 25)
    local n = string.byte(str, y * w + x + 1)
    return n % 2 == 1
end

function is_in_area_id(map_id, x, y, area_id)
    if not MapEx[map_id].rect[area_id] then
        assert(nil, map_id .. " " .. area_id)
    end
    for _, v in ipairs(MapEx[map_id].rect[area_id]) do
        if v[1] < x and x < v[3] and v[2] < y and y < v[4] then
            return true
        end
    end
end

function can_walk(scene_id, x, y)
    local map_id = scene_id_to_map_id[scene_id]
    return map_mgr.can_walk(map_id, x, y)
end

function getFixXY(scene_id, x, y)
    local map_id = scene_id_to_map_id[scene_id]
    return map_mgr.get_fix_xy(map_id, x, y)
end

function getFixXY1(scene_id, x, y,radious)
    if not radious then
        radious = 150
    end
    local map_id = scene_id_to_map_id[scene_id]
    x = x + (math.random(radious,radious*2) - radious)
    y = y + (math.random(radious,radious*2) - radious)
    return map_mgr.get_fix_xy(map_id, x, y)
end

function getDFSPath(scene_id, path, x, y, target_x, target_y)
    local map_id = scene_id_to_map_id[scene_id]
    return map_mgr.get_dfs_path(map_id, path, x, y, target_x, target_y)
end

function getRects(map_id, area_id)
    if not MapEx[map_id].rect then
        assert(nil, map_id .. " " .. area_id)
    end
    if not MapEx[map_id].rect[area_id] then
        assert(nil, map_id .. " " .. area_id)
    end
    return MapEx[map_id].rect[area_id]
end

function through(scene_id, xa, ya, xb, yb)
    local map_id = scene_id_to_map_id[scene_id]
    return map_mgr.through(map_id, xa, ya, xb, yb)
end

function through_jump(scene_id, xa, ya, xb, yb)
    local map_id = scene_id_to_map_id[scene_id]
    return map_mgr.through_jump(map_id, xa, ya, xb, yb)
end

function getWidthHeight(map_id)
    local cf = MapEx[map_id]
    return cf.rect[-1][1][3], cf.rect[-1][1][4]
end

--根据一个地图ID，随机取一个可走点
function getRandPointByMapID(mapID)
    local mapWidth,mapHeight = getWidthHeight(mapID)
    local temX = math.random(1,mapWidth)
    local temY = math.random(1,mapHeight)

    return map_mgr.get_fix_xy(mapID, temX, temY) 
end

--得到区域内随机一点
function getPositionInArea(scene_id, area_id, all)
    local map_id = scene_id_to_map_id[scene_id]
    local rect = nil
    if all then
        local nLen = #MapEx[map_id].rect[area_id]
        rect = MapEx[map_id].rect[area_id][math.random(1, nLen)]
    else
        rect = MapEx[map_id].rect[area_id][1]
    end
    if rect then
        local tx = math.random(rect[1], rect[3])
        local ty = math.random(rect[2], rect[4])
        return map_mgr.get_fix_xy(map_id, tx, ty)
    end
end


function canSpaceJump(human)
    if human.scene_id == nil then
        return
    end

    local mapid = scene_id_to_map_id[human.scene_id]
    local mapConfig = ExcelMap.scene[mapid]
    if mapConfig.can_normal_jump  == 0 then
        return
    end
    for i = 1, #mapConfig.normal_jump_ban do
        if is_in_area_id(mapid, human.x, human.y, mapConfig.normal_jump_ban[i][1]) then
            if mapConfig.normal_jump_ban[i][2] == 2 then
                return
            end
            return true
        end  
    end

    return true, true

end

function getMapTypeByMapID(mapID)
    return ExcelMap.scene[mapID] and ExcelMap.scene[mapID].mapType or nil
end

function getMapTypeBySceneID(sceneID)
    local map_id = scene_id_to_map_id[sceneID]
    if map_id then
        return ExcelMap.scene[map_id].mapType
    end
    return nil
end

init()
